Skip to content

Conversation

@smarcet
Copy link

@smarcet smarcet commented Jan 14, 2026

ref:

https://app.clickup.com/t/86b7ubdfg
https://app.clickup.com/t/86b79918r
https://app.clickup.com/t/86b79918z

Summary by CodeRabbit

  • New Features

    • Sponsor pages management UI with modular Page editor, list page, and Global Page cloning flow.
    • New "Pages" menu item and route for Sponsors; multi-stage dialog for selecting/cloning templates.
  • Improvements

    • Form inputs: datepicker props forwarded, radio group margin configurable, select supports labels, upload supports single-file mode.
    • Added translations and new constants to support pages.
  • Tests

    • Comprehensive tests for page modules UI and interactions.

✏️ Tip: You can customize this high-level summary in your review settings.

tomrndom and others added 2 commits January 14, 2026 19:32
…opup

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
…750)

* feat: page modules generic component, test, adjusting formik inputs

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>

* fix: add implementation for pages modules on sponsor global

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>

---------

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

📝 Walkthrough

Walkthrough

Adds sponsor pages feature: Redux actions/reducer, list and cloning UI, page-template module components/tests, form input tweaks, routing/menu entry, constants, translations, and store wiring.

Changes

Cohort / File(s) Summary
Redux: sponsor pages
src/actions/sponsor-pages-actions.js, src/reducers/sponsors/sponsor-pages-list-reducer.js, src/store.js
New async thunks getSponsorPages and cloneGlobalPage; reducer normalizes pages (tier aggregation, module counts); store registers sponsorPagesListState.
Sponsor pages UI & dialogs
src/pages/sponsors/sponsor-pages-list-page/index.js, src/pages/sponsors/sponsor-pages-list-page/components/global-page/global-page-popup.js, src/pages/sponsors/sponsor-pages-list-page/components/global-page/select-pages-dialog.js
New list page with pagination/sorting/filtering, multi-stage GlobalPagePopup (pages → sponsorships) and SelectPagesDialog for template selection with infinite table and multi-select.
Page template modules & integration
src/pages/sponsors-global/page-templates/page-template-modules-form.js, src/pages/sponsors-global/page-templates/page-template-popup.js, src/actions/page-template-actions.js
New PageModules component (Info/Document/Media modules) with add/reorder/delete and Formik integration; popup uses PageModules and normalizes module ordering; normalizeEntity maps modules, converts upload_deadline to timestamps and strips _tempId.
Tests
src/pages/sponsors-global/page-templates/page-template-module-form.test.js
Comprehensive tests for PageModules covering render, ordering, accordion behavior, deletion and Formik integration.
Form inputs
src/components/mui/formik-inputs/mui-formik-datepicker.js, src/components/mui/formik-inputs/mui-formik-radio-group.js, src/components/mui/formik-inputs/mui-formik-select.js, src/components/mui/formik-inputs/mui-formik-upload.js
DatePicker forwards extra props and sets margin; RadioGroup gains configurable margin prop; Select accepts label and uses InputLabel/shrink logic; Upload adds singleFile mode and normalizes file handling.
Routing & navigation
src/layouts/sponsor-layout.js, src/components/menu/index.js
Route added for /pages in SponsorLayout; menu item sponsor_pages added under sponsors.
Constants & translations
src/utils/constants.js, src/i18n/en.json, .env.example
New constants PAGES_MODULE_KINDS, PAGE_MODULES_MEDIA_TYPES, BADGE_QR_MINIMUM_EXPECTED_FIELDS; extensive i18n entries for sponsor pages; env scopes extended for sponsor pages.

Sequence Diagrams

sequenceDiagram
    participant User
    participant SponsorPagesListPage
    participant Redux
    participant API
    participant Store

    User->>SponsorPagesListPage: Mount / request pages
    SponsorPagesListPage->>Redux: dispatch getSponsorPages(filters)
    Redux->>Redux: dispatch startLoading
    Redux->>API: GET /sponsor_pages with params
    API-->>Redux: pages response
    Redux->>Store: dispatch RECEIVE_SPONSOR_PAGES (normalize)
    Store-->>SponsorPagesListPage: updated props
    Redux->>Redux: dispatch stopLoading
    SponsorPagesListPage-->>User: render pages table
Loading
sequenceDiagram
    participant User
    participant GlobalPagePopup
    participant SelectPagesDialog
    participant SelectSponsorshipsDialog
    participant Redux
    participant API

    User->>GlobalPagePopup: open popup
    GlobalPagePopup->>SelectPagesDialog: show templates stage
    User->>SelectPagesDialog: select templates, click Add
    SelectPagesDialog-->>GlobalPagePopup: onSelectTemplates(ids)
    GlobalPagePopup->>SelectSponsorshipsDialog: show sponsorships stage
    User->>SelectSponsorshipsDialog: choose sponsorships, click Save
    SelectSponsorshipsDialog-->>GlobalPagePopup: onSave(tiers)
    GlobalPagePopup->>Redux: dispatch cloneGlobalPage(templateIds, sponsorIds/tiering)
    Redux->>API: POST /sponsor_pages/clone with payload
    API-->>Redux: success
    Redux->>Redux: dispatch GLOBAL_PAGE_CLONED and getSponsorForms
    Redux-->>GlobalPagePopup: success
    GlobalPagePopup-->>User: show snackbar, close popup
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hopped through modules, templates, and tabs,
I shuffled pages, cloned with no drab,
Accordions opened, deadlines turned to ticks,
Redux and dialogs performed their slick tricks,
A merry rabbit cheers the new sponsor-scene!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feature/summit sponsor pages' directly aligns with the main changeset, which introduces comprehensive sponsor pages functionality including actions, reducers, UI components, and routing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


🧹 Recent nitpick comments
.env.example (1)

20-20: LGTM! Scope expansion aligns with sponsor pages feature.

The addition of show-page/read and show-page/write scopes is appropriate for the new sponsor pages functionality. The naming convention is consistent with existing scope patterns.

Optional: Consider alphabetical ordering of scope variables.

The static analysis tool suggests reordering SPONSOR_PAGES_SCOPES (line 20) to come before SPONSOR_USERS_API_SCOPES (line 16) for alphabetical consistency. This is purely stylistic and doesn't affect functionality.

Based on static analysis hints from dotenv-linter.


📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 325571c and 436d709.

📒 Files selected for processing (1)
  • .env.example
🧰 Additional context used
🪛 dotenv-linter (4.0.0)
.env.example

[warning] 20-20: [UnorderedKey] The SPONSOR_PAGES_SCOPES key should go before the SPONSOR_USERS_API_SCOPES key

(UnorderedKey)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/mui/formik-inputs/mui-formik-datepicker.js (1)

8-8: props is undefined — this will throw a ReferenceError at runtime.

The component destructures only { name, label, required } but line 40 spreads {...props} which doesn't exist in scope.

Additionally, margin is not a valid prop on MUI's DatePicker component — it should remain in slotProps.textField.

🐛 Proposed fix
-const MuiFormikDatepicker = ({ name, label, required }) => {
+const MuiFormikDatepicker = ({ name, label, required, ...props }) => {

And move margin back into slotProps.textField:

          textField: {
            name,
            label: required ? requiredLabel : label,
            error: meta.touched && Boolean(meta.error),
            helperText: meta.touched && meta.error,
-           fullWidth: true
+           fullWidth: true,
+           margin: "normal"
          },
        }}
-       margin="normal"
        // eslint-disable-next-line react/jsx-props-no-spreading
        {...props}

Also applies to: 38-40

src/components/mui/formik-inputs/mui-formik-select.js (1)

74-79: Missing PropTypes definition for label prop.

The component accepts a label prop (line 16) but it's not declared in PropTypes.

Suggested fix
 MuiFormikSelect.propTypes = {
   name: PropTypes.string.isRequired,
   children: PropTypes.node.isRequired,
   placeholder: PropTypes.string,
-  isClearable: PropTypes.bool
+  isClearable: PropTypes.bool,
+  label: PropTypes.string
 };
🤖 Fix all issues with AI agents
In `@src/actions/sponsor-pages-actions.js`:
- Around line 95-133: The cloneGlobalPage action dispatches startLoading() but
never dispatches stopLoading(), leaving the loading state active; update
cloneGlobalPage to dispatch stopLoading() in both the success path (before/after
dispatching getSponsorForms() and snackbarSuccessHandler) and in the
catch/failure path (before handling errors via snackbarErrorHandler) so that
stopLoading() is always called regardless of postRequest resolving or rejecting;
locate the postRequest(...) promise chain in cloneGlobalPage and add
dispatch(stopLoading()) in both the .then and .catch handlers (or use finally)
to guarantee loading is cleared.
- Around line 124-125: The call to getSponsorForms() inside the cloneGlobalPage
promise chain will throw because getSponsorForms is not imported; fix this by
adding an import for getSponsorForms at the top of the file from the module that
exports it (the same module where sponsor form actions live) and keep the
dispatch(getSponsorForms()) call, or if the intent was to refresh sponsor pages
instead, replace that call with the correct exported action (e.g.,
dispatch(getSponsorPages()) )—ensure the chosen function is imported and
referenced (symbols: getSponsorForms, cloneGlobalPage, dispatch).

In `@src/components/mui/formik-inputs/mui-formik-select.js`:
- Around line 34-45: The label can overlap the placeholder because hasValue only
reflects a selected value; update the hasValue logic in the component that
renders InputLabel and Select so it treats a provided placeholder (and
displayEmpty=true) as a value — e.g., compute hasValue = Boolean(field.value) ||
(displayEmpty && Boolean(placeholder)) — then continue using hasValue for
InputLabel's shrink and Select's notched props (symbols: InputLabel, Select,
hasValue, placeholder, displayEmpty, field).

In `@src/components/mui/formik-inputs/mui-formik-upload.js`:
- Line 65: Remove the debug console.log in the UploadFormik component (the line
logging "CHJECK RESPONSE" in mui-formik-upload.js); delete that console.log
statement (or replace it with a proper logger call without the typo if
intentional) so no stray debug output remains in the upload handling code.
- Line 16: Remove the debug console.log statement currently logging "images: "
and field.value inside the MuiFormikUpload component; delete the line
console.log("images: ", field.value) (or replace it with a proper debug-level
logger if persistent logging is required) so no console output remains in
production.
- Around line 93-101: The canAddMore function references mediaType.is_editable
but mediaType lacks that property; update the mediaType object used by this
component (the mediaType declaration near the top of mui-formik-upload.js) to
include is_editable: true or derive it from props, so canAddMore correctly
evaluates editable media types; ensure changes touch the mediaType definition
(not canAddMore) and keep the singleFile and field.value logic intact.

In `@src/layouts/sponsor-layout.js`:
- Line 28: The import for the React component is using camelCase; change the
import identifier from sponsorPagesListPage to PascalCase SponsorPagesListPage
and update all places where sponsorPagesListPage is referenced (e.g., the
component usage currently referencing it) to use SponsorPagesListPage so the
import and usage match the React component naming convention.

In `@src/pages/sponsors-global/page-templates/page-template-modules-form.js`:
- Around line 182-193: InputLabel's htmlFor is incorrectly targeting
buildFieldName("max_file_size") while the actual form control is
MuiFormikTextField with name buildFieldName("description"); update the
InputLabel so htmlFor uses buildFieldName("description") to match the
MuiFormikTextField name and restore proper accessibility (locate the InputLabel
and adjust the buildFieldName argument from "max_file_size" to "description").
- Around line 92-98: The MuiFormikUpload instance inside the
DocumentDownloadModule uses a hardcoded id ("document-module-upload") which will
duplicate across multiple modules; change the id to be unique per module by
deriving it from the module instance (e.g., include the module index, module id
prop, or reuse buildFieldName("file") as part of the id) so each MuiFormikUpload
has a distinct id attribute; update the id prop on the MuiFormikUpload in
page-template-modules-form.js accordingly and ensure any label/for references
use the same generated id.
- Around line 167-178: The InputLabel uses the wrong htmlFor value; change
htmlFor in the InputLabel surrounding the MuiFormikSelect to use
buildFieldName("file_type_id") instead of buildFieldName("max_file_size") so the
label correctly targets the select; update the InputLabel that wraps the
MuiFormikSelect (referencing buildFieldName and the file_type_id field name) to
restore proper label-input association for accessibility.

In
`@src/pages/sponsors/sponsor-pages-list-page/components/global-page/global-page-popup.js`:
- Around line 31-43: The Dialog currently passes the external onClose prop
directly which prevents resetting internal state; change the Dialog's onClose to
call the local handleClose function (the handler that resets stage and
selectedTemplates) instead of the external onClose, so backdrop clicks/Escape
trigger resetting internal state; keep using the external onClose (prop named
onClose) inside handleClose if it needs to notify parents after internal reset.

In `@src/pages/sponsors/sponsor-pages-list-page/index.js`:
- Around line 143-154: The Checkbox is uncontrolled because it lacks a checked
prop tied to the Redux state; update the Checkbox in the FormControlLabel to
pass checked={hideArchived} (ensure hideArchived is derived from Redux state in
this component) so the visual state matches the filter on initial render, and
keep using handleHideArchivedForms to dispatch/toggle the hideArchived value; if
hideArchived can be undefined ensure it is coerced to a boolean (e.g.,
!!hideArchived) before passing to checked.
- Around line 31-42: The component SponsorPagesListPage destructures
getSponsorForm but it isn’t wired to Redux; either add getSponsorForm to the
connect(...) mapDispatchToProps alongside getSponsorPages so the prop is
provided, or remove getSponsorForm from SponsorPagesListPage’s parameter list
and any usages (e.g., handleRowEdit) if the action isn’t implemented; ensure any
call sites like handleRowEdit reference the correctly connected action name.

In `@src/reducers/sponsors/sponsor-pages-list-reducer.js`:
- Around line 61-70: The reducer currently builds sponsorPages with module
counts using hardcoded kinds ("Upload"/"Download") causing
upload_mod/download_mod to be zero; update the modules.filter calls in the
sponsorPages mapping to use the PAGES_MODULE_KINDS constants (e.g.,
PAGES_MODULE_KINDS.DOCUMENT and PAGES_MODULE_KINDS.MEDIA) when computing those
counts, rename the fields from upload_mod/download_mod to document_mod/media_mod
to match the API constants, and propagate those new field names to the
corresponding table column and UI references so they consume document_mod and
media_mod instead of the old names.
🧹 Nitpick comments (15)
src/components/mui/formik-inputs/mui-formik-radio-group.js (1)

13-19: LGTM! Clean addition of configurable margin prop.

The implementation correctly adds an optional margin prop with a sensible default, preserving backward compatibility while enabling layout customization. This aligns with the PR-wide pattern of exposing more customization options on Formik inputs.

Optional: Consider using PropTypes.oneOf to restrict values to MUI's valid options:

-  margin: PropTypes.string,
+  margin: PropTypes.oneOf(["none", "dense", "normal"]),

Also applies to: 25-25, 65-65

src/actions/page-template-actions.js (1)

145-160: Consider adding null-safety for the modules array.

The normalizeEntity function assumes entity.modules is always a valid array. If entity.modules is null or undefined, the .map() call will throw a runtime error.

Suggested defensive check
 const normalizeEntity = (entity) => {
   const normalizedEntity = { ...entity };

-  normalizedEntity.modules = entity.modules.map((module) => {
+  normalizedEntity.modules = (entity.modules || []).map((module) => {
     const normalizedModule = { ...module };

     if (module.kind === PAGES_MODULE_KINDS.MEDIA && module.upload_deadline) {
       normalizedModule.upload_deadline = moment(module.upload_deadline).unix();
     }
     delete normalizedModule._tempId;

     return normalizedModule;
   });

   return normalizedEntity;
 };
src/pages/sponsors/sponsor-pages-list-page/components/global-page/select-pages-dialog.js (3)

140-143: Hardcoded text should use i18n translation.

The text "items selected" is hardcoded in English. For consistency with the rest of the component, this should use T.translate().

Suggested fix
           <Grid2 size={4} sx={{ mt: 1 }}>
-            {selectedRows.length} items selected
+            {T.translate("sponsor_pages.global_page_popup.items_selected", { count: selectedRows.length })}
           </Grid2>

180-183: Consider adding PropTypes for Redux-injected props.

The component receives several props from Redux (pageTemplates, items, currentPage, term, order, orderDir, total, getPageTemplates) but only onClose and onSave are declared in PropTypes. Adding PropTypes for all props improves documentation and development-time validation.


38-40: Add getPageTemplates to useEffect dependency array.

The effect references getPageTemplates but the dependency array is empty. While this works because the function reference is stable (from Redux), adding it satisfies the exhaustive-deps rule and makes the intent clearer.

Suggested fix
   useEffect(() => {
     getPageTemplates("", 1, DEFAULT_PER_PAGE, "id", 1, true);
-  }, []);
+  }, [getPageTemplates]);
src/components/mui/formik-inputs/mui-formik-upload.js (1)

125-128: Missing id in PropTypes.

The id prop is used (line 109) but not declared in PropTypes.

Suggested fix
 MuiFormikUpload.propTypes = {
+  id: PropTypes.string,
   name: PropTypes.string.isRequired,
   singleFile: PropTypes.bool
 };
src/actions/sponsor-pages-actions.js (1)

133-133: Silent catch block may hide errors.

The empty catch block swallows all errors silently. While the comment explains the intent to catch promise rejections, consider at minimum logging the error for debugging purposes.

Suggested fix
-      .catch(() => {}); // need to catch promise reject
+      .catch((err) => {
+        console.error("cloneGlobalPage failed:", err);
+      });
src/pages/sponsors/sponsor-pages-list-page/components/global-page/global-page-popup.js (1)

46-49: Consider adding cloneGlobalPage to PropTypes.

The cloneGlobalPage prop is injected by connect but not declared in PropTypes. While optional, documenting injected props improves maintainability.

Proposed fix
 GlobalPagePopup.propTypes = {
   open: PropTypes.bool.isRequired,
-  onClose: PropTypes.func.isRequired
+  onClose: PropTypes.func.isRequired,
+  cloneGlobalPage: PropTypes.func.isRequired
 };
src/pages/sponsors-global/page-templates/page-template-popup.js (1)

160-162: Remove unnecessary formik prop from PageModules.

The PageModules component uses useFormikContext() internally to access formik state (as seen in the relevant code snippet). The formik prop passed here is unused.

Proposed fix
             <Box sx={{ py: 2 }}>
-              <PageModules formik={formik} />
+              <PageModules />
             </Box>
src/pages/sponsors-global/page-templates/page-template-module-form.test.js (2)

64-64: Minor: Comment in Spanish should be in English for consistency.

The comment "Mock DragAndDropList que captura onReorder" should be in English to maintain consistency with the rest of the codebase.

Suggested fix
-// Mock DragAndDropList que captura onReorder
+// Mock DragAndDropList that captures onReorder

303-316: Consider: Tests rely on MUI implementation details.

Tests use .closest(".MuiAccordionSummary-root") which couples them to MUI's class naming. This could break if MUI changes its class names in a future version. Consider adding data-testid attributes to the AccordionSummary components in the actual implementation for more resilient testing.

src/pages/sponsors/sponsor-pages-list-page/index.js (3)

45-47: Missing dependency in useEffect.

The useEffect hook uses getSponsorPages but doesn't include it in the dependency array. While this works because getSponsorPages is a stable reference from Redux, ESLint's react-hooks/exhaustive-deps rule would flag this.

Suggested fix
   useEffect(() => {
     getSponsorPages();
-  }, []);
+  }, [getSponsorPages]);

63-71: Stubbed handlers with console.log statements.

handleRowDelete and handleArchiveItem contain console.log statements and commented-out code. These appear to be work-in-progress placeholders. Consider adding TODO comments or removing the console.log before merging.

Suggested change
   const handleRowDelete = (itemId) => {
-    console.log("DELETE ITEM ID...", itemId);
-    // deleteSponsorForm(itemId);
+    // TODO: Implement delete functionality
+    // deleteSponsorPage(itemId);
   };

-  const handleArchiveItem = (item) => console.log("archive ITEM...", item);
-  // item.is_archived
-  //   ? unarchiveSponsorForm(item.id)
-  //   : archiveSponsorForm(item.id);
+  const handleArchiveItem = (item) => {
+    // TODO: Implement archive functionality
+    // item.is_archived ? unarchiveSponsorPage(item.id) : archiveSponsorPage(item.id);
+  };

205-212: Commented code for FormPagePopup.

The FormPagePopup component is commented out, indicating incomplete functionality. Consider adding a TODO comment to track this work item.

src/i18n/en.json (1)

2644-2658: Minor: Inconsistent casing in translation value.

Line 2648 has "name": "name" (lowercase) while the corresponding column header on line 2632 has "name_column_label": "Name" (capitalized). The popup's name field label should likely be capitalized for consistency.

Suggested fix
     "global_page_popup": {
       "title": "Add Page Template",
       "items_selected": "items selected",
       "code": "Code",
-      "name": "name",
+      "name": "Name",
       "info_mod": "Info Mod",
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e86b7a and 325571c.

📒 Files selected for processing (18)
  • src/actions/page-template-actions.js
  • src/actions/sponsor-pages-actions.js
  • src/components/menu/index.js
  • src/components/mui/formik-inputs/mui-formik-datepicker.js
  • src/components/mui/formik-inputs/mui-formik-radio-group.js
  • src/components/mui/formik-inputs/mui-formik-select.js
  • src/components/mui/formik-inputs/mui-formik-upload.js
  • src/i18n/en.json
  • src/layouts/sponsor-layout.js
  • src/pages/sponsors-global/page-templates/page-template-module-form.test.js
  • src/pages/sponsors-global/page-templates/page-template-modules-form.js
  • src/pages/sponsors-global/page-templates/page-template-popup.js
  • src/pages/sponsors/sponsor-pages-list-page/components/global-page/global-page-popup.js
  • src/pages/sponsors/sponsor-pages-list-page/components/global-page/select-pages-dialog.js
  • src/pages/sponsors/sponsor-pages-list-page/index.js
  • src/reducers/sponsors/sponsor-pages-list-reducer.js
  • src/store.js
  • src/utils/constants.js
🧰 Additional context used
🧬 Code graph analysis (8)
src/store.js (1)
src/reducers/sponsors/sponsor-pages-list-reducer.js (1)
  • sponsorPagesListReducer (33-83)
src/components/menu/index.js (1)
src/reducers/notes/notes-reducer.js (1)
  • summitId (48-48)
src/pages/sponsors-global/page-templates/page-template-popup.js (2)
src/pages/sponsors-global/page-templates/page-template-modules-form.js (2)
  • modules (200-200)
  • PageModules (198-343)
src/utils/constants.js (4)
  • PAGES_MODULE_KINDS (245-249)
  • PAGES_MODULE_KINDS (245-249)
  • PAGE_MODULES_MEDIA_TYPES (251-254)
  • PAGE_MODULES_MEDIA_TYPES (251-254)
src/components/mui/formik-inputs/mui-formik-select.js (6)
src/components/mui/formik-inputs/mui-formik-datepicker.js (1)
  • field (9-9)
src/components/mui/formik-inputs/mui-formik-upload.js (1)
  • field (14-14)
src/components/mui/formik-inputs/mui-formik-textfield.js (1)
  • field (7-7)
src/components/mui/formik-inputs/sponsorship-input-mui.js (2)
  • field (31-31)
  • field (37-37)
src/components/mui/formik-inputs/mui-formik-checkbox.js (1)
  • field (12-12)
src/components/mui/formik-inputs/company-input-mui.js (2)
  • field (32-32)
  • field (38-38)
src/actions/page-template-actions.js (1)
src/utils/constants.js (2)
  • PAGES_MODULE_KINDS (245-249)
  • PAGES_MODULE_KINDS (245-249)
src/components/mui/formik-inputs/mui-formik-upload.js (1)
src/utils/constants.js (6)
  • MAX_INVENTORY_IMAGE_UPLOAD_SIZE (190-190)
  • MAX_INVENTORY_IMAGE_UPLOAD_SIZE (190-190)
  • MAX_INVENTORY_IMAGES_UPLOAD_QTY (191-191)
  • MAX_INVENTORY_IMAGES_UPLOAD_QTY (191-191)
  • ALLOWED_INVENTORY_IMAGE_FORMATS (192-192)
  • ALLOWED_INVENTORY_IMAGE_FORMATS (192-192)
src/pages/sponsors-global/page-templates/page-template-modules-form.js (3)
src/utils/constants.js (4)
  • PAGE_MODULES_MEDIA_TYPES (251-254)
  • PAGE_MODULES_MEDIA_TYPES (251-254)
  • PAGES_MODULE_KINDS (245-249)
  • PAGES_MODULE_KINDS (245-249)
src/components/mui/showConfirmDialog.js (1)
  • showConfirmDialog (5-42)
src/components/mui/dnd-list.js (1)
  • DragAndDropList (16-72)
src/reducers/sponsors/sponsor-pages-list-reducer.js (1)
src/actions/sponsor-pages-actions.js (4)
  • REQUEST_SPONSOR_PAGES (31-31)
  • REQUEST_SPONSOR_PAGES (31-31)
  • RECEIVE_SPONSOR_PAGES (32-32)
  • RECEIVE_SPONSOR_PAGES (32-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (22)
src/components/mui/formik-inputs/mui-formik-select.js (2)

1-12: LGTM!

The new imports for IconButton and InputLabel are appropriate for the added label and clearable functionality.


29-30: LGTM!

The hasValue guard correctly checks for non-empty values, handling empty string, undefined, and null cases appropriately.

src/utils/constants.js (1)

244-254: LGTM!

The new constants are well-organized and follow the existing patterns in this file. PAGES_MODULE_KINDS and PAGE_MODULES_MEDIA_TYPES provide clear, centralized definitions for module kinds and media types used throughout the sponsor pages feature.

src/actions/page-template-actions.js (1)

14-33: LGTM!

The new imports for moment-timezone and PAGES_MODULE_KINDS are correctly placed and support the enhanced module normalization logic.

src/actions/sponsor-pages-actions.js (1)

68-72: Verify the filter logic for sponsorship types.

The filter uses sponsorship_type_id_not_in which appears to exclude rather than include the specified sponsorship types. Confirm this is the intended behavior for the use case.

src/components/mui/formik-inputs/mui-formik-upload.js (1)

76-91: Verify the property name of the imageFile parameter passed by UploadInputV2's onRemove callback.

The filter on line 84 compares i.filename !== imageFile.name, but without access to the external UploadInputV2 library (from openstack-uicore-foundation), it cannot be definitively confirmed whether imageFile.name is correct. Cross-checking with similar code in inventory-item-form.js and form-template-form.js shows identical patterns, but this could indicate either consistent behavior or a systemic issue. Confirm the property name that UploadInputV2 passes to the onRemove callback—if it's not name, the filter will fail silently and files won't be removed from the array.

src/reducers/sponsors/sponsor-pages-list-reducer.js (2)

21-31: LGTM!

The DEFAULT_STATE structure is well-defined with appropriate defaults for pagination, sorting, and filtering.


36-52: LGTM!

Proper handling of state reset on summit change/logout and request state management with cleared data during loading.

src/store.js (2)

170-170: LGTM!

Import correctly added for the new sponsor pages list reducer.


254-254: LGTM!

New state slice properly registered following the existing naming conventions and placed logically with other sponsor-related state.

src/pages/sponsors/sponsor-pages-list-page/components/global-page/global-page-popup.js (1)

25-29: LGTM!

Good use of .finally() to ensure cleanup regardless of success or failure.

src/layouts/sponsor-layout.js (1)

67-87: LGTM!

The route structure correctly follows the existing pattern with proper breadcrumb integration and nested Switch/Route setup.

src/pages/sponsors-global/page-templates/page-template-popup.js (3)

33-41: LGTM!

Good extraction of common module creation logic into addModule helper. The temporary ID and order assignment pattern is clean.


43-70: LGTM!

Module handlers properly initialize all required fields for each module type using the extracted addModule helper and imported constants.


72-89: LGTM!

Good approach to normalize module ordering on submit by mapping indices to custom_order, ensuring consistent ordering regardless of user reordering during editing.

src/components/menu/index.js (1)

247-251: LGTM!

The new sponsor_pages menu item follows the established pattern for other sponsor-related menu items, with appropriate routing and access control via admin-sponsors.

src/pages/sponsors-global/page-templates/page-template-module-form.test.js (1)

83-116: LGTM!

The test helpers renderWithFormik and createModule are well-designed and provide good isolation for testing the PageModules component. The beforeEach block properly resets state between tests.

src/pages/sponsors-global/page-templates/page-template-modules-form.js (3)

240-243: Consider: Module ordering may have gaps after deletion.

After deleting a module, the custom_order values of remaining modules are not recalculated. For example, if modules have orders [0, 1, 2] and module at index 1 is deleted, the remaining modules will have orders [0, 2]. This may or may not be intentional depending on how the backend handles ordering.

Verify if the backend expects contiguous custom_order values or if gaps are acceptable. If contiguous values are required, consider recalculating orders after deletion:

if (isConfirmed) {
  const updated = modules
    .filter((_, i) => i !== index)
    .map((m, i) => ({ ...m, custom_order: i }));
  setFieldValue(name, updated);
}

198-211: LGTM!

The auto-scroll functionality using refs and useEffect is well-implemented. It correctly detects when a new module is added and scrolls to make it visible.


319-342: LGTM!

The main render logic correctly handles the empty state, renders the DragAndDropList with appropriate configuration, and includes the scroll target element.

src/i18n/en.json (2)

2626-2643: LGTM!

The new sponsor_pages translation object is well-structured with appropriate keys for the list page, column labels, filters, and empty state message.


3950-3964: LGTM!

The new module-related translation keys under page_template_list.page_crud are comprehensive and cover all the module types (Info, Document, Media) and their fields.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +95 to +133
export const cloneGlobalPage =
(pagesIds, sponsorIds, allSponsors) => async (dispatch, getState) => {
const { currentSummitState } = getState();
const accessToken = await getAccessTokenSafely();
const { currentSummit } = currentSummitState;

dispatch(startLoading());

const params = {
access_token: accessToken
};

const normalizedEntity = {
page_template_ids: pagesIds,
sponsorship_types: sponsorIds,
apply_to_all_types: allSponsors
};

if (allSponsors) {
delete normalizedEntity.sponsorship_types;
}

return postRequest(
null,
createAction(GLOBAL_PAGE_CLONED),
`${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/show-pages/clone`,
normalizedEntity,
snackbarErrorHandler
)(params)(dispatch)
.then(() => {
dispatch(getSponsorForms());
dispatch(
snackbarSuccessHandler({
title: T.translate("general.success"),
html: T.translate("sponsor_pages.global_page_popup.success")
})
);
})
.catch(() => {}); // need to catch promise reject
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing stopLoading() dispatch in cloneGlobalPage.

startLoading() is dispatched at line 101, but stopLoading() is never called. This will leave the loading state active indefinitely after the clone operation completes (success or failure).

Suggested fix
     return postRequest(
       null,
       createAction(GLOBAL_PAGE_CLONED),
       `${window.SPONSOR_PAGES_API_URL}/api/v1/summits/${currentSummit.id}/show-pages/clone`,
       normalizedEntity,
       snackbarErrorHandler
     )(params)(dispatch)
       .then(() => {
         dispatch(getSponsorForms());
         dispatch(
           snackbarSuccessHandler({
             title: T.translate("general.success"),
             html: T.translate("sponsor_pages.global_page_popup.success")
           })
         );
       })
-      .catch(() => {}); // need to catch promise reject
+      .catch(() => {})
+      .finally(() => {
+        dispatch(stopLoading());
+      });
   };
🤖 Prompt for AI Agents
In `@src/actions/sponsor-pages-actions.js` around lines 95 - 133, The
cloneGlobalPage action dispatches startLoading() but never dispatches
stopLoading(), leaving the loading state active; update cloneGlobalPage to
dispatch stopLoading() in both the success path (before/after dispatching
getSponsorForms() and snackbarSuccessHandler) and in the catch/failure path
(before handling errors via snackbarErrorHandler) so that stopLoading() is
always called regardless of postRequest resolving or rejecting; locate the
postRequest(...) promise chain in cloneGlobalPage and add
dispatch(stopLoading()) in both the .then and .catch handlers (or use finally)
to guarantee loading is cleared.

Comment on lines +124 to +125
.then(() => {
dispatch(getSponsorForms());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

getSponsorForms is not imported - this will cause a runtime error.

The function getSponsorForms() is called but never imported. This will throw a ReferenceError at runtime when cloneGlobalPage succeeds.

Suggested fix

Add the missing import at the top of the file:

+import { getSponsorForms } from "./sponsor-actions";

Or, if the intent was to refresh sponsor pages instead:

       .then(() => {
-        dispatch(getSponsorForms());
+        dispatch(getSponsorPages());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.then(() => {
dispatch(getSponsorForms());
.then(() => {
dispatch(getSponsorPages());
🤖 Prompt for AI Agents
In `@src/actions/sponsor-pages-actions.js` around lines 124 - 125, The call to
getSponsorForms() inside the cloneGlobalPage promise chain will throw because
getSponsorForms is not imported; fix this by adding an import for
getSponsorForms at the top of the file from the module that exports it (the same
module where sponsor form actions live) and keep the dispatch(getSponsorForms())
call, or if the intent was to refresh sponsor pages instead, replace that call
with the correct exported action (e.g., dispatch(getSponsorPages()) )—ensure the
chosen function is imported and referenced (symbols: getSponsorForms,
cloneGlobalPage, dispatch).

Comment on lines +34 to +45
{label && (
<InputLabel htmlFor={name} id={`${name}-label`} shrink={hasValue}>
{label}
</InputLabel>
)}
<Select
name={name}
// eslint-disable-next-line react/jsx-props-no-spreading
{...field}
labelId={`${name}-label`}
label={label}
notched={hasValue}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n src/components/mui/formik-inputs/mui-formik-select.js

Repository: fntechgit/summit-admin

Length of output: 2702


🏁 Script executed:

rg "MuiFormikSelect" --type js --type jsx -A 3 -B 1 | head -100

Repository: fntechgit/summit-admin

Length of output: 93


🏁 Script executed:

rg "MuiFormikSelect" -A 3 -B 1 | head -150

Repository: fntechgit/summit-admin

Length of output: 12188


🏁 Script executed:

rg "MuiFormikSelect" -B 5 -A 8 "src/components/mui/formik-inputs/additional-input/additional-input.js"

Repository: fntechgit/summit-admin

Length of output: 1516


Label and placeholder text may visually overlap when no value is selected.

When displayEmpty is true and a placeholder is provided (currently used in src/components/mui/formik-inputs/additional-input/additional-input.js), the InputLabel will remain un-shrunk (inside the select box) while the placeholder text displays in the same area. This causes visual overlap.

The label should always shrink when a placeholder is present:

Suggested fix
  const hasValue =
    field.value !== "" && field.value !== undefined && field.value !== null;
+ const shouldShrink = hasValue || Boolean(placeholder);

  return (
    <FormControl fullWidth error={meta.touched && Boolean(meta.error)}>
      {label && (
-        <InputLabel htmlFor={name} id={`${name}-label`} shrink={hasValue}>
+        <InputLabel htmlFor={name} id={`${name}-label`} shrink={shouldShrink}>
          {label}
        </InputLabel>
      )}
      <Select
        name={name}
        // eslint-disable-next-line react/jsx-props-no-spreading
        {...field}
        labelId={`${name}-label`}
        label={label}
-        notched={hasValue}
+        notched={shouldShrink}
🤖 Prompt for AI Agents
In `@src/components/mui/formik-inputs/mui-formik-select.js` around lines 34 - 45,
The label can overlap the placeholder because hasValue only reflects a selected
value; update the hasValue logic in the component that renders InputLabel and
Select so it treats a provided placeholder (and displayEmpty=true) as a value —
e.g., compute hasValue = Boolean(field.value) || (displayEmpty &&
Boolean(placeholder)) — then continue using hasValue for InputLabel's shrink and
Select's notched props (symbols: InputLabel, Select, hasValue, placeholder,
displayEmpty, field).

const MuiFormikUpload = ({ id, name, onImageDeleted, singleFile = false }) => {
const [field, meta, helpers] = useField(name);

console.log("images: ", field.value);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug console.log statement.

This debug statement should be removed before merging to production.

Suggested fix
-  console.log("images: ", field.value);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log("images: ", field.value);
🤖 Prompt for AI Agents
In `@src/components/mui/formik-inputs/mui-formik-upload.js` at line 16, Remove the
debug console.log statement currently logging "images: " and field.value inside
the MuiFormikUpload component; delete the line console.log("images: ",
field.value) (or replace it with a proper debug-level logger if persistent
logging is required) so no console output remains in production.

filename: response.name
};
helpers.setValue([...field.value, image]);
console.log("CHJECK RESPONSE", response);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debug console.log with typo.

Debug statement with typo ("CHJECK" → "CHECK") should be removed before merging.

Suggested fix
-      console.log("CHJECK RESPONSE", response);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log("CHJECK RESPONSE", response);
🤖 Prompt for AI Agents
In `@src/components/mui/formik-inputs/mui-formik-upload.js` at line 65, Remove the
debug console.log in the UploadFormik component (the line logging "CHJECK
RESPONSE" in mui-formik-upload.js); delete that console.log statement (or
replace it with a proper logger call without the typo if intentional) so no
stray debug output remains in the upload handling code.

Comment on lines +182 to +193
<Grid2 size={12}>
<InputLabel htmlFor={buildFieldName("max_file_size")}>
{T.translate("page_template_list.page_crud.description")}
</InputLabel>
<MuiFormikTextField
name={buildFieldName("description")}
fullWidth
multiline
margin="none"
rows={2}
/>
</Grid2>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

htmlFor attribute points to wrong field name.

The InputLabel on line 183 has htmlFor={buildFieldName("max_file_size")} but the associated field is description. This is another copy-paste error affecting accessibility.

Suggested fix
       <Grid2 size={12}>
-        <InputLabel htmlFor={buildFieldName("max_file_size")}>
+        <InputLabel htmlFor={buildFieldName("description")}>
           {T.translate("page_template_list.page_crud.description")}
         </InputLabel>
         <MuiFormikTextField
           name={buildFieldName("description")}
           fullWidth
           multiline
           margin="none"
           rows={2}
         />
       </Grid2>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Grid2 size={12}>
<InputLabel htmlFor={buildFieldName("max_file_size")}>
{T.translate("page_template_list.page_crud.description")}
</InputLabel>
<MuiFormikTextField
name={buildFieldName("description")}
fullWidth
multiline
margin="none"
rows={2}
/>
</Grid2>
<Grid2 size={12}>
<InputLabel htmlFor={buildFieldName("description")}>
{T.translate("page_template_list.page_crud.description")}
</InputLabel>
<MuiFormikTextField
name={buildFieldName("description")}
fullWidth
multiline
margin="none"
rows={2}
/>
</Grid2>
🤖 Prompt for AI Agents
In `@src/pages/sponsors-global/page-templates/page-template-modules-form.js`
around lines 182 - 193, InputLabel's htmlFor is incorrectly targeting
buildFieldName("max_file_size") while the actual form control is
MuiFormikTextField with name buildFieldName("description"); update the
InputLabel so htmlFor uses buildFieldName("description") to match the
MuiFormikTextField name and restore proper accessibility (locate the InputLabel
and adjust the buildFieldName argument from "max_file_size" to "description").

Comment on lines +31 to +43
return (
<Dialog open={open} onClose={onClose} maxWidth={dialogSize} fullWidth>
{stage === "pages" && (
<SelectPagesDialog
onSave={handleOnSelectTemplates}
onClose={handleClose}
/>
)}
{stage === "sponsorships" && (
<SelectSponsorshipsDialog onSave={handleOnSave} onClose={handleClose} />
)}
</Dialog>
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use handleClose for Dialog's onClose prop to reset internal state.

The Dialog's onClose uses onClose directly instead of handleClose. When the user closes the dialog via backdrop click or escape key, the internal state (stage, selectedTemplates) won't be reset, potentially causing stale state on next open.

Proposed fix
-    <Dialog open={open} onClose={onClose} maxWidth={dialogSize} fullWidth>
+    <Dialog open={open} onClose={handleClose} maxWidth={dialogSize} fullWidth>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<Dialog open={open} onClose={onClose} maxWidth={dialogSize} fullWidth>
{stage === "pages" && (
<SelectPagesDialog
onSave={handleOnSelectTemplates}
onClose={handleClose}
/>
)}
{stage === "sponsorships" && (
<SelectSponsorshipsDialog onSave={handleOnSave} onClose={handleClose} />
)}
</Dialog>
);
return (
<Dialog open={open} onClose={handleClose} maxWidth={dialogSize} fullWidth>
{stage === "pages" && (
<SelectPagesDialog
onSave={handleOnSelectTemplates}
onClose={handleClose}
/>
)}
{stage === "sponsorships" && (
<SelectSponsorshipsDialog onSave={handleOnSave} onClose={handleClose} />
)}
</Dialog>
);
🤖 Prompt for AI Agents
In
`@src/pages/sponsors/sponsor-pages-list-page/components/global-page/global-page-popup.js`
around lines 31 - 43, The Dialog currently passes the external onClose prop
directly which prevents resetting internal state; change the Dialog's onClose to
call the local handleClose function (the handler that resets stage and
selectedTemplates) instead of the external onClose, so backdrop clicks/Escape
trigger resetting internal state; keep using the external onClose (prop named
onClose) inside handleClose if it needs to notify parents after internal reset.

Comment on lines +31 to +42
const SponsorPagesListPage = ({
sponsorPages,
currentPage,
perPage,
term,
order,
orderDir,
hideArchived,
totalCount,
getSponsorPages,
getSponsorForm
}) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

getSponsorForm is destructured but not connected via Redux.

The getSponsorForm action is destructured from props on line 41 but is not included in the connect call on lines 221-223. This will cause getSponsorForm to be undefined when handleRowEdit is called, resulting in a runtime error.

Suggested fix

Either add getSponsorForm to the connect call:

-export default connect(mapStateToProps, {
-  getSponsorPages
+import { getSponsorPages, getSponsorForm } from "../../../actions/sponsor-pages-actions";
+
+export default connect(mapStateToProps, {
+  getSponsorPages,
+  getSponsorForm
 })(SponsorPagesListPage);

Or remove it from the destructured props if not yet implemented.

🤖 Prompt for AI Agents
In `@src/pages/sponsors/sponsor-pages-list-page/index.js` around lines 31 - 42,
The component SponsorPagesListPage destructures getSponsorForm but it isn’t
wired to Redux; either add getSponsorForm to the connect(...) mapDispatchToProps
alongside getSponsorPages so the prop is provided, or remove getSponsorForm from
SponsorPagesListPage’s parameter list and any usages (e.g., handleRowEdit) if
the action isn’t implemented; ensure any call sites like handleRowEdit reference
the correctly connected action name.

Comment on lines +143 to +154
<FormControlLabel
control={
<Checkbox
onChange={handleHideArchivedForms}
inputProps={{
"aria-label": T.translate("sponsor_pages.hide_archived")
}}
/>
}
label={T.translate("sponsor_pages.hide_archived")}
/>
</FormGroup>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Checkbox is uncontrolled - missing checked prop.

The "Hide archived" checkbox doesn't have a checked prop bound to the hideArchived state from Redux. This means the checkbox visual state won't reflect the actual filter state, especially on initial render.

Suggested fix
             <FormControlLabel
               control={
                 <Checkbox
+                  checked={hideArchived}
                   onChange={handleHideArchivedForms}
                   inputProps={{
                     "aria-label": T.translate("sponsor_pages.hide_archived")
                   }}
                 />
               }
               label={T.translate("sponsor_pages.hide_archived")}
             />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<FormControlLabel
control={
<Checkbox
onChange={handleHideArchivedForms}
inputProps={{
"aria-label": T.translate("sponsor_pages.hide_archived")
}}
/>
}
label={T.translate("sponsor_pages.hide_archived")}
/>
</FormGroup>
<FormControlLabel
control={
<Checkbox
checked={hideArchived}
onChange={handleHideArchivedForms}
inputProps={{
"aria-label": T.translate("sponsor_pages.hide_archived")
}}
/>
}
label={T.translate("sponsor_pages.hide_archived")}
/>
🤖 Prompt for AI Agents
In `@src/pages/sponsors/sponsor-pages-list-page/index.js` around lines 143 - 154,
The Checkbox is uncontrolled because it lacks a checked prop tied to the Redux
state; update the Checkbox in the FormControlLabel to pass
checked={hideArchived} (ensure hideArchived is derived from Redux state in this
component) so the visual state matches the filter on initial render, and keep
using handleHideArchivedForms to dispatch/toggle the hideArchived value; if
hideArchived can be undefined ensure it is coerced to a boolean (e.g.,
!!hideArchived) before passing to checked.

Comment on lines +61 to +70
const sponsorPages = payload.response.data.map((a) => ({
id: a.id,
code: a.code,
name: a.name,
tier: a.sponsorship_types.map((s) => s.name).join(", "),
info_mod: a.modules.filter((m) => m.kind === "Info").length,
upload_mod: a.modules.filter((m) => m.kind === "Upload").length,
download_mod: a.modules.filter((m) => m.kind === "Download").length,
is_archived: a.is_archived
}));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find the PAGES_MODULE_KINDS constant definition
rg -n "PAGES_MODULE_KINDS" --type=js -A 5 -B 2

Repository: fntechgit/summit-admin

Length of output: 26166


🏁 Script executed:

#!/bin/bash
# Search for module kind string values used in filtering/comparisons
rg -n "kind\s*[=!:]\s*['\"]" --type=js | head -50

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Look for module kind references in API responses or types
rg -n "\"kind\":|'kind':" --type=js -A 2 | head -100

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Check the sponsor pages file structure for context
fd "sponsor" --type=f -e js | head -20

Repository: fntechgit/summit-admin

Length of output: 1011


🏁 Script executed:

#!/bin/bash
# Check the actual sponsor pages list reducer file
cat -n src/reducers/sponsors/sponsor-pages-list-reducer.js | sed -n '55,75p'

Repository: fntechgit/summit-admin

Length of output: 865


🏁 Script executed:

#!/bin/bash
# Also check what the property names should be and any usages of these properties
rg -n "upload_mod|download_mod|document_mod|media_mod" --type=js

Repository: fntechgit/summit-admin

Length of output: 2388


🏁 Script executed:

#!/bin/bash
# Look for API response examples or test data
rg -n "kind.*:.*['\"]" src/pages/sponsors-global/page-templates/page-template-module-form.test.js -A 2 | head -30

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Search for sponsor pages API examples in actions or tests
fd "sponsor-pages" --type=f -e js | xargs rg -l "modules"

Repository: fntechgit/summit-admin

Length of output: 117


🏁 Script executed:

#!/bin/bash
# Check page-template-list-reducer for comparison
cat -n src/reducers/sponsors_inventory/page-template-list-reducer.js | sed -n '70,85p'

Repository: fntechgit/summit-admin

Length of output: 728


🏁 Script executed:

#!/bin/bash
# Check if there are any test fixtures or mock data for sponsor pages
fd "sponsor-pages" --type=f -e test.js | xargs cat | head -200

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Look for API response mock data in sponsor pages actions
rg -n "modules.*\[" src/actions/sponsor-pages-actions.js -A 5 | head -50

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Check if "Upload" and "Download" strings appear anywhere else as module kinds
rg -n '"Upload"|"Download"' --type=js | grep -i module

Repository: fntechgit/summit-admin

Length of output: 605


🏁 Script executed:

#!/bin/bash
# Check the UI components that use these properties to understand intent
rg -n "upload_mod|download_mod" src/pages/sponsors/sponsor-pages-list-page/index.js -B 2 -A 2

Repository: fntechgit/summit-admin

Length of output: 387


🏁 Script executed:

#!/bin/bash
# Check what the actual API might return - look at sponsor-pages-actions
cat src/actions/sponsor-pages-actions.js | head -100

Repository: fntechgit/summit-admin

Length of output: 3182


🏁 Script executed:

#!/bin/bash
# Look for API response examples, mocks, or fixtures
fd . --type=f -e json | xargs rg -l "modules" | head -10

Repository: fntechgit/summit-admin

Length of output: 95


🏁 Script executed:

#!/bin/bash
# Search for test data or fixtures related to sponsor pages
find . -path ./node_modules -prune -o -type f \( -name "*.test.js" -o -name "*.spec.js" -o -name "*fixture*" -o -name "*mock*" \) -print | xargs rg -l "sponsor.*page" 2>/dev/null | head -10

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Check if there's any documentation or comments about module kinds
rg -n "Info|Upload|Download" src/actions/sponsor-pages-actions.js -B 2 -A 2

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Check if the API endpoint documentation or swagger is available
fd . --type=f \( -name "swagger*" -o -name "openapi*" -o -name "*.yaml" -o -name "*.yml" \) 2>/dev/null | head -10

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Check i18n for module kind labels
rg -n "upload_mod|download_mod|document_module|media_module|Info|Upload|Download" src/i18n/en.json | head -30

Repository: fntechgit/summit-admin

Length of output: 1625


🏁 Script executed:

#!/bin/bash
# Check if there's backend API documentation or type definitions
fd . --type=f -e ts | xargs rg -l "modules|kind" 2>/dev/null | head -10

Repository: fntechgit/summit-admin

Length of output: 505


🏁 Script executed:

#!/bin/bash
# Look at the complete page-template-list-reducer file structure for comparison
cat -n src/reducers/sponsors_inventory/page-template-list-reducer.js | head -100

Repository: fntechgit/summit-admin

Length of output: 3581


🏁 Script executed:

#!/bin/bash
# Verify what properties are accessed from sponsorPages in the UI
rg -n "sponsorPages" src/pages/sponsors/sponsor-pages-list-page/index.js -A 3 -B 3 | head -50

Repository: fntechgit/summit-admin

Length of output: 863


🏁 Script executed:

#!/bin/bash
# Check if there are any actual API examples with modules data structure
rg -n "modules.*\[" --type=js -A 10 | grep -A 10 "kind"

Repository: fntechgit/summit-admin

Length of output: 4340


Fix module kind filtering to use constants instead of hardcoded strings.

The reducer filters modules using hardcoded strings "Upload" and "Download", but the PAGES_MODULE_KINDS constant defines the actual API values as "Document" and "Media". This mismatch causes upload_mod and download_mod to always be 0.

Update to use the constants for consistency and correctness:

Suggested fix
+import { PAGES_MODULE_KINDS } from "../../utils/constants";
...
-        info_mod: a.modules.filter((m) => m.kind === "Info").length,
-        upload_mod: a.modules.filter((m) => m.kind === "Upload").length,
-        download_mod: a.modules.filter((m) => m.kind === "Download").length,
+        info_mod: a.modules.filter((m) => m.kind === PAGES_MODULE_KINDS.INFO).length,
+        document_mod: a.modules.filter((m) => m.kind === PAGES_MODULE_KINDS.DOCUMENT).length,
+        media_mod: a.modules.filter((m) => m.kind === PAGES_MODULE_KINDS.MEDIA).length,

Note: Also update the corresponding table columns and UI references to use document_mod and media_mod instead of upload_mod and download_mod.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const sponsorPages = payload.response.data.map((a) => ({
id: a.id,
code: a.code,
name: a.name,
tier: a.sponsorship_types.map((s) => s.name).join(", "),
info_mod: a.modules.filter((m) => m.kind === "Info").length,
upload_mod: a.modules.filter((m) => m.kind === "Upload").length,
download_mod: a.modules.filter((m) => m.kind === "Download").length,
is_archived: a.is_archived
}));
const sponsorPages = payload.response.data.map((a) => ({
id: a.id,
code: a.code,
name: a.name,
tier: a.sponsorship_types.map((s) => s.name).join(", "),
info_mod: a.modules.filter((m) => m.kind === PAGES_MODULE_KINDS.INFO).length,
document_mod: a.modules.filter((m) => m.kind === PAGES_MODULE_KINDS.DOCUMENT).length,
media_mod: a.modules.filter((m) => m.kind === PAGES_MODULE_KINDS.MEDIA).length,
is_archived: a.is_archived
}));
🤖 Prompt for AI Agents
In `@src/reducers/sponsors/sponsor-pages-list-reducer.js` around lines 61 - 70,
The reducer currently builds sponsorPages with module counts using hardcoded
kinds ("Upload"/"Download") causing upload_mod/download_mod to be zero; update
the modules.filter calls in the sponsorPages mapping to use the
PAGES_MODULE_KINDS constants (e.g., PAGES_MODULE_KINDS.DOCUMENT and
PAGES_MODULE_KINDS.MEDIA) when computing those counts, rename the fields from
upload_mod/download_mod to document_mod/media_mod to match the API constants,
and propagate those new field names to the corresponding table column and UI
references so they consume document_mod and media_mod instead of the old names.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants